import pandas as pd
import numpy as np
import logging
from typing import List, Dict
import os

class ColdStartRecommender:
    """
    冷启动推荐器 - 处理新用户或新电影的推荐
    """
    
    def __init__(self, data_path: str = "data/processed/", content_recommender=None, enable_content: bool = True):
        self.data_path = data_path
        self.logger = logging.getLogger(__name__)
        self.popular_movies = None
        self.diverse_movies = None
        self.content_recommender = content_recommender
        self._load_popular_movies()
        # 初始化或复用内容推荐器
        if self.content_recommender is not None:
            self.logger.info("✅ 内容推荐器已注入并可用")
        elif enable_content:
            try:
                from .content_recommender import ContentRecommender
                self.content_recommender = ContentRecommender(
                    movie_features_path=os.path.join(self.data_path, 'movie_features.pkl'),
                    movies_metadata_path=os.path.join(self.data_path, 'movies_cleaned.pkl')
                )
                self.logger.info("✅ 内容推荐器初始化完成")
            except Exception as e:
                self.logger.warning(f"内容推荐器初始化失败: {e}")
    
    def _load_popular_movies(self):
        """加载热门电影数据"""
        try:
            # 加载电影特征和元数据
            movie_features = pd.read_pickle(os.path.join(self.data_path, 'movie_features.pkl'))
            movies_metadata = pd.read_pickle(os.path.join(self.data_path, 'movies_cleaned.pkl'))
            
            # 合并数据
            self.popular_movies = movies_metadata.merge(
                movie_features[['movieId', 'rating_count', 'rating_mean']], 
                on='movieId', how='left'
            )
            
            # 计算流行度分数（基于评分数量和平均分）
            # 先填充缺失，确保计算稳定
            if 'rating_count' in self.popular_movies.columns:
                self.popular_movies['rating_count'] = pd.to_numeric(self.popular_movies['rating_count'], errors='coerce').fillna(0)
            else:
                self.popular_movies['rating_count'] = 0
            if 'rating_mean' in self.popular_movies.columns:
                self.popular_movies['rating_mean'] = pd.to_numeric(self.popular_movies['rating_mean'], errors='coerce').fillna(0)
            else:
                self.popular_movies['rating_mean'] = 0
            # 规范 movieId 类型
            self.popular_movies['movieId'] = pd.to_numeric(self.popular_movies['movieId'], errors='coerce').astype('Int64')
            self.popular_movies = self.popular_movies.dropna(subset=['movieId'])
            self.popular_movies['movieId'] = self.popular_movies['movieId'].astype(int)
            self.popular_movies['popularity_score'] = (
                self.popular_movies['rating_count'] * self.popular_movies['rating_mean']
            )
            
            # 排序
            self.popular_movies = self.popular_movies.sort_values(
                'popularity_score', ascending=False
            )
            
            self.logger.info("✅ 冷启动推荐器初始化完成")
            
        except Exception as e:
            self.logger.error(f"❌ 冷启动推荐器初始化失败: {e}")
    
    def recommend_popular(self, top_k: int = 10) -> List[Dict]:
        """内容+热门推荐混合：优先内容推荐补足后用热门兜底"""
        if self.popular_movies is None:
            return []
        recommendations = []
        used_movie_ids = set()
        # 1. 内容推荐（随机选一部热门电影作为参考）
        if self.content_recommender is not None and len(self.popular_movies) > 0:
            import random
            ref_movie = self.popular_movies.head(100).sample(1).iloc[0]  # 从前100热门中随机选一部
            ref_movie_id = ref_movie['movieId']
            content_recs = self.content_recommender.recommend_similar_to(ref_movie_id, top_k=top_k)
            for rec in content_recs:
                rec_full = {
                    'movieId': rec['movieId'],
                    'title': rec['title'],
                    'genres': self.popular_movies[self.popular_movies['movieId'] == rec['movieId']]['genres'].values[0] if rec['movieId'] in self.popular_movies['movieId'].values else '',
                    'popularity_score': None,
                    # 将内容推荐的相似度映射到 predicted_rating 字段，保留 reason 字段
                    'predicted_rating': round(float(rec.get('similarity', 0)), 2) if rec.get('similarity', None) is not None else None,
                    'year': self.popular_movies[self.popular_movies['movieId'] == rec['movieId']]['year'].values[0] if 'year' in self.popular_movies.columns and rec['movieId'] in self.popular_movies['movieId'].values else '未知',
                    'reason': f'内容推荐（相似度） 参考电影ID:{ref_movie_id}'
                }
                recommendations.append(rec_full)
                used_movie_ids.add(rec['movieId'])
        # 2. 热门推荐补足
        top_movies = self.popular_movies.head(top_k * 2)
        used_genres = set()
        selected_movies = []
        for _, movie in top_movies.iterrows():
            if movie['movieId'] in used_movie_ids:
                continue
            genres = set(movie['genres'].split('|')) if isinstance(movie['genres'], str) else set()
            if len(selected_movies) < top_k:
                selected_movies.append(movie)
                used_genres.update(genres)
            else:
                if not genres.issubset(used_genres):
                    selected_movies.append(movie)
                    used_genres.update(genres)
            if len(selected_movies) + len(recommendations) >= top_k:
                break
        for movie in selected_movies:
            recommendations.append({
                'movieId': movie['movieId'],
                'title': movie['title'],
                'genres': movie['genres'],
                'popularity_score': round(movie['popularity_score'], 2),
                'predicted_rating': round(movie['rating_mean'], 2) if 'rating_mean' in movie and not pd.isnull(movie['rating_mean']) else None,
                'year': movie.get('year', '未知') if hasattr(movie, 'get') else movie['year'] if 'year' in movie else '未知',
                'reason': '热门推荐'
            })
        return recommendations[:top_k]
    
    def recommend_diverse(self, top_k: int = 10) -> List[Dict]:
        """推荐多样化电影（不同类型）"""
        if self.popular_movies is None:
            return []
        
        # 按类型分组，从每个类型中选择热门电影
        genre_movies = {}
        for _, movie in self.popular_movies.iterrows():
            genres = movie['genres'].split('|') if isinstance(movie['genres'], str) else ['Unknown']
            
            for genre in genres:
                if genre not in genre_movies:
                    genre_movies[genre] = []
                genre_movies[genre].append(movie)
        
        # 从每个类型中选择一部电影
        recommendations = []
        for genre, movies in genre_movies.items():
            if movies and len(recommendations) < top_k:
                best_movie = movies[0]  # 每个类型中最热门的
                recommendations.append({
                    'movieId': best_movie['movieId'],
                    'title': best_movie['title'],
                    'genres': best_movie['genres'],
                    'popularity_score': round(best_movie['popularity_score'], 2),
                    'predicted_rating': round(best_movie['rating_mean'], 2) if 'rating_mean' in best_movie and not pd.isnull(best_movie['rating_mean']) else None,
                    'year': best_movie.get('year', '未知') if hasattr(best_movie, 'get') else best_movie['year'] if 'year' in best_movie else '未知',
                    'reason': f'{genre}类型热门'
                })
        
        # 如果类型不够，补充热门电影
        if len(recommendations) < top_k:
            additional = self.recommend_popular(top_k - len(recommendations))
            recommendations.extend(additional)
        
        return recommendations[:top_k]